home *** CD-ROM | disk | FTP | other *** search
/ The Original Shareware 1.1 / The Original Shareware (WeMake CDs)(Volume 1.1)(CDs, Inc)(1993).iso / 19 / madtrb11.zip / DOS2IO-3.INC < prev    next >
Text File  |  1985-08-17  |  21KB  |  811 lines

  1. (*
  2.                           Dos2io-3.inc
  3.  
  4.  
  5.     Dedicated to the public domain.
  6.  
  7.         -- Cole Brecheen
  8.            17 August 1985
  9. *)
  10. {$V-} {Relaxes type checking on string parameters.}
  11. {$U-,C-}{Enables keyboard buffering.}
  12.  
  13.  
  14. PROCEDURE SetPtrFromEnd( FileHandle: INTEGER;
  15.                  OffSetFromEnd: REAL );
  16. LABEL EndProcedure;
  17. VAR
  18.   TmpPtr : BufferPtr;
  19.   rgstr : RegPack;
  20.   lngthnum : REAL;
  21.   segnum : INTEGER;
  22. BEGIN {SetPtrFromEnd}
  23.   lngthnum := FileLength( FileHandle );
  24.   IF lngthnum = 0
  25.     THEN GOTO EndProcedure;
  26.   WITH rgstr DO BEGIN
  27.     a.h := $42; {command to move file read/write pointer}
  28.     a.l := 0;
  29.     {Zero in a.l means that pointer moves to offset bytes
  30.     from the beginnning of the file.}
  31.     b.x := FileHandle;
  32.  
  33.     RealToSegmented( lngthnum + OffSetFromEnd, c.x, d.x );
  34.     msdos( rgstr );
  35.     IF FlaggedError( flags )
  36.       THEN BEGIN { writeln('setptrfromend error'); } {diag}
  37.         PrintMessage( MessageType( a.x ) );
  38.       END;
  39.   END; {WITH rgstr}
  40.  
  41.   CheckInitialization;
  42.     {From here down we're flushing any buffer that
  43.     corresponds with this filehandle.}
  44.  
  45.   IF BufLstBase = nil
  46.     THEN GOTO EndProcedure;
  47.   TmpPtr := BufLstBase;
  48.   WHILE (TmpPtr^.next <> nil)
  49.     and
  50.     (TmpPtr^.handle <> FileHandle) DO TmpPtr := TmpPtr^.next;
  51.   IF TmpPtr^.handle = FileHandle
  52.     THEN  tmpPtr^.ndx := BufSize + 1;
  53.   EndProcedure:
  54. END; {SetPtrFromEnd}
  55.  
  56.  
  57. PROCEDURE SetPtrFromStart( FileHandle: INTEGER;
  58.                    OffSetFromStart : REAL );
  59. LABEL
  60.   EndProcedure;
  61. VAR
  62.   TmpPtr : BufferPtr;
  63.   rgstr : RegPack;
  64. BEGIN {SetPtrFromStart}
  65.   WITH rgstr DO BEGIN
  66.     a.h := $42;
  67.     {command to move file read/write pointer}
  68.     a.l := 0;
  69.     {moves pointer to offset bytes from the beginnning of the
  70.     file}
  71.     b.x := FileHandle;
  72.     RealToSegmented( OffSetFromStart, c.x, d.x );
  73.     msdos( rgstr );
  74.     IF FlaggedError( flags )
  75.       THEN BEGIN { writeln('setptrfromstart error'); } {diag}
  76.         PrintMessage( MessageType( a.x ) );
  77.       END;
  78.   END; {WITH rgstr}
  79.  
  80.   CheckInitialization;
  81.   IF BufLstBase = nil
  82.     THEN GOTO EndProcedure;
  83.   TmpPtr := BufLstBase;
  84.   WHILE (TmpPtr^.next <> nil)
  85.     and
  86.     (TmpPtr^.handle <> FileHandle) DO TmpPtr := TmpPtr^.next;
  87.   IF TmpPtr^.handle = FileHandle
  88.     THEN  tmpPtr^.ndx := BufSize + 1;
  89.   EndProcedure:
  90. END; {SetPtrFromStart}
  91.  
  92.  
  93.  
  94. FUNCTION BytesToWord( lobyte, hibyte: INTEGER ): INTEGER;
  95.     {Takes two bytes and stores them in a single word.  The
  96.     last byte in the parameter list is most significant.}
  97. BEGIN {BytesToWord}
  98.   hibyte := swap( hibyte );
  99.       {Reverses the order of the two bytes in hibyte.}
  100.   BytesToWord := lobyte OR hibyte;
  101.       {OR does bitwise addition in this language.}
  102. END; {BytesToWord}
  103.  
  104.  
  105. PROCEDURE BitRangeToInt( TheSet: BitSet;
  106.                  LowBit, HighBit: INTEGER;
  107.                  VAR answer:  INTEGER );
  108.       {BitRangeToInt lets us specify the bits that store the
  109.       value in which we're interested, and it loads that value
  110.       into the last parameter in the list.}
  111.  
  112.   FUNCTION power(x,n:INTEGER): INTEGER;
  113.     {Returns x raised to the nth power.}
  114.   VAR w,z,i: INTEGER;
  115.   BEGIN
  116.     w := x; i := n;
  117.     z := 1;
  118.     WHILE i <> 0 DO
  119.     BEGIN
  120.       IF ODD(i) THEN z := z*w;
  121.       i := i DIV 2;
  122.       IF i <> 0
  123.     THEN w := w*w;
  124.     END;
  125.     power := z;
  126.   END; {power}
  127.  
  128. VAR
  129.   tmp: RECORD
  130.      CASE BOOLEAN of
  131.        true: ( IntForm : INTEGER );
  132.        false:( SetForm : BitSet );
  133.        END;
  134.   cnt : INTEGER;
  135. BEGIN {BitRangeToInt}
  136.   tmp.SetForm := TheSet;
  137.   {Bit 15 is most significant.}
  138.   answer := 0;
  139.   FOR cnt := LowBit TO HighBit DO
  140.     BEGIN
  141.       IF cnt IN tmp.SetForm
  142.     THEN answer := answer + power( 2,  cnt - LowBit );
  143.     END;
  144. END;  {BitRangeToInt}
  145.  
  146.  
  147. PROCEDURE IntegerToDate( TheInt: INTEGER;
  148.                  VAR month, day, year: INTEGER );
  149. VAR
  150.   TheSet : BitSet;
  151. BEGIN {IntegerToDate}
  152.   IntegerToBitSet( TheInt, TheSet );
  153.   BitRangeToInt( TheSet, 0, 4, day );
  154.   BitRangeToInt( TheSet, 5, 8, month );
  155.   BitRangeToInt( TheSet, 9, 15, year );
  156.   year := year + 80;
  157. END; {IntegerToDate}
  158.  
  159.  
  160.  
  161. FUNCTION DateToInteger( month, day, year: INTEGER ): INTEGER;
  162. VAR
  163.   BitSet1, BitSet2: BitSet;
  164.   TmpResult: integer;
  165.   buf: BitRange;
  166. BEGIN {DateToInteger}
  167.   IF year > 1900 THEN
  168.     year := year - 1900;
  169.   IF year in [80 .. 199] THEN
  170.     year := year - 80
  171.   ELSE abort( 'Invalid year: ' + IntStr(year,0) );
  172.   IF not (month in [1..12]) THEN
  173.     abort( 'Invalid month: ' + IntStr(month,0) );
  174.   IF not (day in [1..31]) THEN
  175.     abort( 'Invalid day: ' + IntStr(day,0) );
  176.  
  177.  
  178.   IntegerToBitSet( 0, BitSet2 );
  179.   IntegerToBitSet( day, BitSet1 );
  180.   FOR buf := 0 to 15 DO
  181.     BEGIN
  182.       IF buf in BitSet1
  183.     THEN BitSet2 := BitSet2 + [buf];
  184.     END;
  185.  
  186.   IntegerToBitSet( month, BitSet1 );
  187.   FOR buf := 0 to 15 DO
  188.     BEGIN
  189.       IF buf in BitSet1
  190.     THEN BitSet2 := BitSet2 + [buf + 5];
  191.     END;
  192.  
  193.   IntegerToBitSet( year, BitSet1 );
  194.   FOR buf := 0 to 15 DO
  195.     BEGIN
  196.       IF buf in BitSet1
  197.     THEN BitSet2 := BitSet2 + [buf + 9];
  198.     END;
  199.  
  200.   BitSetToInteger( BitSet2, TmpResult );
  201.   DateToInteger := TmpResult;
  202. END; {DateToInteger}
  203.  
  204.  
  205.  
  206. PROCEDURE IntegerToTime( TheInteger: INTEGER;
  207.                  VAR hours, minutes, seconds : INTEGER );
  208. VAR
  209.   TheSet : BitSet;
  210. BEGIN {IntegerToTime}
  211.   IntegerToBitSet( TheInteger, TheSet );
  212.   BitRangeToInt( TheSet, 11, 15, hours );
  213.   BitRangeToInt( TheSet, 5, 10, minutes );
  214.   BitRangeToInt( TheSet, 0, 4, seconds );
  215.   seconds := seconds * 2;
  216.     {We double seconds because the operating system stores
  217.     this value in two-second increments.}
  218. END; {IntegerToTime}
  219.  
  220.  
  221.  
  222. FUNCTION TimeToInteger( hours,
  223.                 minutes,
  224.                 seconds: INTEGER ): INTEGER;
  225. VAR
  226.   BitSet1, BitSet2: BitSet;
  227.   TmpResult: integer;
  228.   buf: BitRange;
  229. BEGIN {TimeToInteger}
  230.   IF not (hours in [0..23]) THEN
  231.     abort( 'Invalid hour: ' + IntStr(hours,0) );
  232.   IF not( minutes in [0..59] ) THEN
  233.     abort( 'Invalid minute: ' + IntStr(minutes,0) );
  234.   IF not( seconds in [0..59] ) THEN
  235.     abort( 'Invalid second: ' + IntStr(seconds,0) );
  236.  
  237.   IntegerToBitSet( 0, BitSet2 );
  238.   IntegerToBitSet( seconds div 2, BitSet1 );
  239.   FOR buf := 0 to 15 DO
  240.     BEGIN
  241.       IF buf in BitSet1
  242.     THEN BitSet2 := BitSet2 + [buf];
  243.     END;
  244.  
  245.   IntegerToBitSet( minutes, BitSet1 );
  246.   FOR buf := 0 to 15 DO
  247.     BEGIN
  248.       IF buf in BitSet1
  249.     THEN BitSet2 := BitSet2 + [buf + 5];
  250.     END;
  251.  
  252.   IntegerToBitSet( hours, BitSet1 );
  253.   FOR buf := 0 to 15 DO
  254.     BEGIN
  255.       IF buf in BitSet1
  256.     THEN BitSet2 := BitSet2 + [buf + 11];
  257.     END;
  258.  
  259.   BitSetToInteger( BitSet2, TmpResult );
  260.   TimeToInteger := TmpResult;
  261. END; {TimeToInteger}
  262.  
  263.  
  264.  
  265. PROCEDURE AddToFile( FileName : dos2str80; FileHandle : INTEGER;
  266.              message  : dos2str255 );
  267. VAR
  268.   SavedMessage  : ErrorMessage;
  269.   AlreadyOpen   : BOOLEAN;
  270.  
  271. BEGIN  {AddToFile}
  272.   IF FileName = null
  273.     THEN abort( 'Always pass a file name to AddToFile.' );
  274.   IF FileHandle > 4 {see D-15 of PC-DOS manual}
  275.     THEN
  276.       BEGIN
  277.     AlreadyOpen := TRUE;
  278.       END
  279.     ELSE
  280.       BEGIN
  281.     AlreadyOpen := FALSE;
  282.     SavedMessage := OpenFile( FileHandle, FileName );
  283.     IF SavedMessage <> NoError
  284.       THEN printmessage( SavedMessage );
  285.       END;
  286.  
  287.   SetPtrFromEnd( FileHandle, -1 );
  288.     {We move to -1 because we want to insert this data just
  289.     before the eof char.}
  290.  
  291.   AddStr( message, #26 );
  292.     {This will be the new eof marker.}
  293.   WriteStr( FileHandle, message );
  294.   PrintMessage( CloseHandle( FileHandle ) );
  295.     {updates the file length}
  296.   IF AlreadyOpen
  297.     THEN
  298.       BEGIN
  299.     SavedMessage := OpenFile( FileHandle, FileName );
  300.     IF SavedMessage <> NoError
  301.       THEN printmessage( SavedMessage );
  302.       END;
  303. END;  {AddToFile} 
  304.  
  305.  
  306.  
  307.  PROCEDURE SetFileDateAndTime( FileHandle,
  308.                       month, day, year,
  309.                       hours, minutes, seconds: INTEGER );
  310. VAR
  311.   rgstr : RegPack;
  312. BEGIN {SetFileDateAndTime}
  313.   WITH rgstr DO
  314.     BEGIN
  315.       a.h := $57;
  316.       a.l := 1;
  317.       b.x := FileHandle;
  318.       d.x := DateToInteger( month, day, year );
  319.       c.x := TimeToInteger( hours, minutes, seconds );
  320.     {The reason for the swaps is that the bytes are reversed
  321.     when date and time values are passed in registers.}
  322.       msdos( rgstr );
  323.       IF FlaggedError( flags )
  324.     THEN PrintMessage( MessageType( a.x ) );
  325.     END; {WITH rgstr}
  326. END; {SetFileDateAndTime}
  327.  
  328.  
  329.  
  330.  PROCEDURE GetFileDateAndTime( FileHandle: INTEGER;
  331.                       VAR month, day, year,
  332.                       hours, minutes, seconds: INTEGER );
  333. VAR
  334.   rgstr : RegPack;
  335. BEGIN {GetFileDateAndTime}
  336.   WITH rgstr DO
  337.     BEGIN
  338.       a.h := $57;
  339.       a.l := 0;
  340.       b.x := FileHandle;
  341.       msdos( rgstr );
  342.       IF FlaggedError( flags ) THEN
  343.     PrintMessage( MessageType( a.x ) )
  344.       ELSE
  345.     BEGIN
  346.       IntegerToDate( d.x, month, day, year );
  347.       IntegerToTime( c.x, hours, minutes, seconds );
  348.     END;
  349.     END; {WITH rgstr}
  350. END; {GetFileDateAndTime}
  351.  
  352.  
  353.  PROCEDURE LoadDTAinfo( VAR tmpstr : dos2str255 );
  354.     {Pulls information about files from an area of memory
  355.     called the Disk Transfer Address (DTA).  Used in both
  356.     FindFirstFile and FindNextFile, below.}
  357.  
  358.   PROCEDURE ExtractTime( TheInteger : INTEGER;
  359.                  VAR TheStr : dos2str80 );
  360.   TYPE
  361.     str8 = STRING[8];
  362.   VAR
  363.     hours, minutes, seconds : INTEGER;
  364.     TheSet : BitSet;
  365.     MinStr : str8;
  366.     pm : BOOLEAN;
  367.   BEGIN {ExtractTime}
  368.     IntegerToTime( TheInteger, hours, minutes, seconds );
  369.     {From here down we're formatting TheStr so that the
  370.     string returned by LoadDTAinfo will look nice if it's
  371.     written.}
  372. {
  373.     str( seconds:2, SecStr );
  374.     IF SecStr[1] = ' '
  375.       THEN SecStr[1] := '0';
  376.     You can add this back in if you're interested in the
  377.     seconds part of the file's time.
  378. }
  379.     str( minutes:2, MinStr );
  380.     IF MinStr[1] = ' '
  381.       THEN MinStr[1] := '0';
  382.     pm := hours > 12;
  383.     IF pm
  384.       THEN hours := hours - 12;
  385.     TheStr := concat( '  ', IntStr(hours,2), ':', MinStr );
  386.     IF pm
  387.       THEN TheStr := concat( TheStr, 'p' )
  388.       ELSE TheStr := concat( TheStr, 'a' );
  389.   END;  {ExtractTime}
  390.  
  391.  
  392.   PROCEDURE ExtractDate( TheInteger : INTEGER;
  393.                  VAR TheStr : dos2str80 );
  394.   TYPE
  395.     str8 = STRING[8];
  396.   VAR
  397.     month, day, year : INTEGER;
  398.     TheSet : BitSet;
  399.     YrStr, MnthStr, DayStr : str8;
  400.   BEGIN {ExtractDate}
  401.     IntegerToDate( TheInteger, month, day, year );
  402.     str( day:2, DayStr );
  403.     IF DayStr[1] = ' '
  404.       THEN DayStr[1] := '0';
  405.     TheStr := concat( IntStr(month,2),
  406.               '-', DayStr,
  407.               '-', IntStr(year,2) );
  408.   END;  {ExtractDate}
  409.  
  410. LABEL 1;
  411. TYPE
  412.   str32 = STRING[32];
  413. VAR
  414.   rgstr : RegPack;
  415.   FileSize : REAL;
  416.   DTAinfo : dos2str255;
  417.   SizeStr,
  418.   datestr,
  419.   timestr : str32;
  420.   SubDirCode,
  421.   cnt,
  422.   index : INTEGER;
  423.   LoWord, HiWord : INTEGER;
  424. BEGIN {LoadDTAinfo}
  425.   SizeStr := null;
  426.   DateStr := null;
  427.   TimeStr := null;
  428.  
  429.   WITH rgstr DO BEGIN
  430.     a.h := $2F; {get Disk Transfer Address}
  431.     msdos( rgstr );
  432.       {ES:BX now contains the DTA}
  433.     FillChar( DTAinfo, sizeof( DTAinfo ), CHR(0) );
  434.     FOR cnt := 0 TO 42 DO
  435.        mem[seg(DTAinfo):ofs(DTAinfo) + cnt] := mem[ES:b.x + cnt];
  436.     {Transfers 43 bytes from the DTA into DTAinfo.}
  437.   END; {WITH rgstr}
  438.  
  439.   SubDirCode := ord(DTAinfo[21]) and $10;
  440.     {This sets SubDirCode to 16--that is, it turns on the
  441.     fourth bit of SubDirCode--if the file found is a
  442.     directory entry.  Otherwise, SubDirCode is set to 0.}
  443.  
  444.   IF SubDirCode <> 0 {That is, if the file is a sub-directory.}
  445.     THEN SizeStr := '   <DIR>'
  446.     ELSE
  447.       BEGIN
  448.     LoWord := BytesToWord( ORD(DTAinfo[26]), ORD(DTAinfo[27]) );
  449.     HiWord := BytesToWord( ORD(DTAinfo[28]), ORD(DTAinfo[29]) );
  450.     FileSize := SegmentedToReal( HiWord, LoWord );
  451.     str( FileSize:0:0, SizeStr );
  452.     WHILE length(SizeStr) < 8
  453.       DO insert( ' ', SizeStr, 1 );
  454.       END;
  455.  
  456.   ExtractTime( BytesToWord( ORD(DTAinfo[22]), ORD(DTAinfo[23]) ),
  457.            timestr );
  458.   ExtractDate( BytesToWord( ORD(DTAinfo[24]), ORD(DTAinfo[25]) ),
  459.            datestr );
  460.   WHILE length(DateStr) < 10
  461.     DO insert( ' ', DateStr, 1 );
  462.  
  463.   tmpstr := null;
  464.   FOR index := 30 TO 43 DO
  465.     BEGIN
  466.       tmpstr[0] := succ( tmpstr[0] );
  467.       IF DTAinfo[ index ] = CHR(0)
  468.     THEN
  469.       BEGIN
  470.         tmpstr[0] := pred( tmpstr[0] );
  471.         GOTO 1;
  472.       END
  473.     ELSE tmpstr[ index - 29 ] := DTAinfo[ index ];
  474.     END;
  475.   1:
  476.   WHILE length(TmpStr) < 12
  477.     DO TmpStr := concat( TmpStr, ' ' );
  478.   TmpStr := concat( TmpStr, SizeStr, datestr, timestr );
  479. END;  {LoadDTAinfo}
  480.  
  481.  
  482.  
  483.  
  484.  
  485. TYPE
  486.   DTAptr = ^DTA;
  487.   DTA = array [1..128] of byte;
  488. VAR
  489.   DTAseg, DTAofs : INTEGER;
  490.   TmpDTAptr : DTAptr;
  491.  
  492. PROCEDURE SaveDTA;
  493. VAR
  494.   rgstr : RegPack;
  495. BEGIN {SaveDTA}
  496.   WITH rgstr DO BEGIN
  497.     a.h := $2F;
  498.     msdos( rgstr );
  499.     DTAseg := es;
  500.     DTAofs := b.x;
  501.     new( TmpDTAptr );
  502.     ds := seg( TmpDTAptr^ );
  503.     d.x := ofs( TmpDTAptr^ );
  504.     a.h := $1A;
  505.   END; {WITH rgstr}
  506.   msdos( rgstr );
  507. END; {SaveDTA}
  508.  
  509. PROCEDURE RestoreDTA;
  510. VAR
  511.   rgstr : RegPack;
  512. BEGIN {RestoreDTA}
  513.   dispose( TmpDTAptr );
  514.   rgstr.ds := DTAseg;
  515.   rgstr.d.x := DTAofs;
  516.   rgstr.a.h := $1A;
  517.   msdos( rgstr );
  518. END; {RestoreDTA}
  519.  
  520.  
  521.  FUNCTION FindFirstFile(FileName: dos2str80;
  522.                VAR FileInfo: dos2str255): ErrorMessage;
  523. VAR
  524.   tmpset : BitSet;
  525.   rgstr : RegPack;
  526.   tmpstr : dos2str80;
  527. BEGIN {FindFirstFile}
  528.   FindFirstFile := NoError;
  529.   FileInfo := null;
  530.   tmpstr := FileName;
  531.   MakeAsciiZ( tmpstr );
  532.   WITH rgstr DO BEGIN
  533.     d.x := ofs( tmpstr );
  534.     ds := seg( tmpstr );
  535.     TmpSet := [0,1,2,4];
  536.     {We set four attribute bits:  read-only file, hidden
  537.     file, system file, and sub-directory.  This allows us to
  538.     find any of these files, plus normal files.}
  539.     BitSetToInteger( TmpSet, c.x );
  540.     a.h := $4E; {find first matching file}
  541.     msdos( rgstr );
  542.     IF FlaggedError( flags )
  543.       THEN FindFirstFile := MessageType( a.x )
  544.       ELSE LoadDTAinfo( FileInfo );
  545.   END; {WITH rgstr}
  546. END; {FindFirstFile}
  547.  
  548.  
  549.  FUNCTION FindNextFile( VAR FileInfo : dos2str255 ): ErrorMessage;
  550. VAR
  551.   rgstr : RegPack;
  552. BEGIN
  553.   FindNextFile := NoError;
  554.   FileInfo := null;
  555.   WITH rgstr DO BEGIN
  556.     a.h := $4F; {find next matching file}
  557.     flags := 0;
  558.     msdos( rgstr );
  559.     IF FlaggedError( flags )
  560.       THEN FindNextFile := MessageType( a.x )
  561.       ELSE LoadDTAinfo( FileInfo );
  562.   END; {WITH rgstr}
  563. END; {FindNextFile}
  564.  
  565.  
  566.  
  567.  FUNCTION VolumeLabel( TheDrive: CHAR ): dos2str80;
  568.     {Returns the label of the disk in TheDrive.}
  569. VAR
  570.   rgstr : RegPack;
  571.   XFCB : RECORD
  572.        prfx : array [1..7] of byte;
  573.        fcb : array [0..36] of byte;
  574.      END;
  575.     {XFCB is an "Extended File Control Block." }
  576.   bufstr : dos2str80;
  577.   cnt : INTEGER;
  578. BEGIN {VolumeLabel}
  579.   lowerch( TheDrive );
  580.   bufstr := null;
  581.   fillchar( XFCB, sizeof(XFCB), '?' );
  582.     {We fill XFCB with question marks because function $11
  583.     does not take any other kind of wildcard.  The PC-DOS
  584.     documentation says that question-mark wildcards are
  585.     allowed, but neglects to mention that they are
  586.     mandatory.}
  587.   WITH XFCB DO
  588.     BEGIN
  589.       prfx[1] := $FF; {indicates an extended FCB}
  590.       prfx[7] := $8; {attribute set to volume label}
  591.       IF TheDrive = 'z'
  592.     THEN fcb[0] := 0
  593.     ELSE fcb[0] := ord(TheDrive) - 96 ;
  594.     {sets 'a' to 1, 'b' to 2, etc.}
  595.     END;
  596.  
  597.   WITH rgstr DO BEGIN
  598.     ds := seg( XFCB );
  599.     d.x := ofs( XFCB );
  600.     a.h := $11; {Search for first entry.}
  601.     msdos( rgstr );
  602.     IF a.l = $FF
  603.       THEN bufstr := 'unlabelled vol'
  604.       ELSE
  605.     BEGIN
  606.       a.h := $2F; {get Disk Transfer Address}
  607.       msdos( rgstr );
  608.         {ES:BX now contains the DTA}
  609.       FOR cnt := 8 to 18 DO
  610.         AddStr( bufstr, chr(mem[es:b.x + cnt]) );
  611.     {We do this because information from the search gets
  612.     transferred into the DTA, not the extended file control
  613.     block whose address we passed going into function $11.}
  614.     END;
  615.   END; {WITH rgstr}
  616.   VolumeLabel := bufstr;
  617. END; {VolumeLabel}
  618.  
  619.  
  620.  
  621. FUNCTION FreeDiskSpace( DriveLetter: CHAR;
  622.                 VAR FreeBytes: REAL ): ErrorMessage;
  623. CONST
  624.   upperdifference = 32;
  625. VAR
  626.   rgstr : RegPack;
  627. BEGIN
  628.   FreeDiskSpace := NoError;
  629.   FreeBytes := 0;
  630.   lowerch( DriveLetter );
  631.   IF not (DriveLetter in ['a'..'z'])
  632.     THEN halt;
  633.   WITH rgstr DO BEGIN
  634.     IF DriveLetter = 'z' {'z' means default drive}
  635.       THEN d.l := 0
  636.       ELSE d.l := ord( DriveLetter ) - 96;
  637.     {turns an A into a 1, etc}
  638.     a.h := $36;
  639.     msdos( rgstr );
  640.     IF a.x = $FFFF THEN
  641.       FreeDiskSpace := InvalidDrive
  642.     {AX returns $FFFF if the drive number was invalid.
  643.     Otherwise, BX contains the number of available clusters,
  644.     DX contains the total number of bytes per sector, and AX
  645.     contains the number of sectors per cluster.}
  646.     ELSE
  647.       BEGIN
  648.     FreeBytes := WordToReal(b.x) * WordToReal(a.x);
  649.     FreeBytes := FreeBytes * WordToReal(c.x);
  650.     {Division of this operation into two lines only reduces
  651.     the width of the listing.}
  652.       END;
  653.   END; {WITH rgstr}
  654. END; {FreeDiskSpace}
  655.  
  656.  
  657.  
  658.  FUNCTION CopyFile( OldHandle: integer;
  659.            NewFileName: dos2str80 ): ErrorMessage;
  660. TYPE
  661.   memptr = RECORD
  662.          addr : ^integer;
  663.          size : INTEGER;
  664.        END;
  665.  
  666. VAR
  667.   TheDrive : char;
  668.   MemoryPtr : memptr;
  669.   NewHandle : INTEGER;
  670.   SavedMessage : ErrorMessage;
  671.   FreeBytes,
  672.   FileSize,
  673.   BytesToBeFreed,
  674.   BytesToRead : REAL;
  675.  
  676.  
  677.   PROCEDURE ReadOldFile( VAR MemoryPtr : memptr );
  678.   LABEL EndProcedure;
  679.   VAR
  680.     rgstr : regpack;
  681.  
  682.     function min( first, second: real ): real;
  683.     begin {min}
  684.       if first < second
  685.     then min := first
  686.     else min := second;
  687.     end; {min}
  688.  
  689.   BEGIN {ReadOldFile}
  690.     IF BytesToRead <= 0 THEN
  691.       BEGIN
  692.     MemoryPtr.addr := nil;
  693.     GOTO EndProcedure;
  694.       END;
  695.     with MemoryPtr DO
  696.       BEGIN
  697.     size := RealToWord( min( maxavail * 16,
  698.                          min( SegSize - 1,
  699.                               BytesToRead)));
  700.     GetMem( addr, size );
  701.     rgstr.c.x := size;
  702.           {CX gets number of bytes to read.}
  703.     rgstr.a.h := $3F;  {DOS Read From file Code}
  704.     rgstr.b.x := OldHandle;
  705.     rgstr.d.x := ofs( addr^ );
  706.     rgstr.ds := seg( addr^ );
  707.       END;
  708.     msdos( rgstr );
  709.     IF FlaggedError( rgstr.flags )
  710.       THEN printmessage( messagetype( rgstr.a.x ) )
  711.       ELSE BytesToRead := BytesToRead - MemoryPtr.size;
  712.     EndProcedure:
  713.   END; {ReadOldFile}
  714.  
  715.  
  716.   PROCEDURE WriteNewFile( MemoryPtr : MemPtr );
  717.   LABEL EndProcedure;
  718.   VAR
  719.     rgstr : regpack;
  720.   BEGIN {WriteNewFile}
  721.     IF MemoryPtr.addr = nil
  722.       THEN GOTO EndProcedure;
  723.  
  724.     rgstr.b.x := NewHandle;
  725.     rgstr.c.x := MemoryPtr.size;
  726.     rgstr.ds := seg( MemoryPtr.addr^ );
  727.     rgstr.d.x := ofs( MemoryPtr.addr^ );
  728.     rgstr.a.h := $40;  {Write to a file or device.}
  729.     msdos( rgstr );
  730.     IF rgstr.a.x < rgstr.c.x
  731.       {if fewer than c.x bytes were actually written}
  732.       THEN
  733.     BEGIN
  734.       CopyFile := AccessDenied;
  735.       SavedMessage := CloseHandle( NewHandle );
  736.       WriteStr( outp, 'No room.' );
  737.       halt;
  738.     END;
  739.     FreeMem( MemoryPtr.addr, MemoryPtr.size );
  740.     EndProcedure:
  741.   END; {WriteNewFile}
  742.  
  743.  
  744.  
  745.   PROCEDURE SetDateAndTime;
  746.   VAR
  747.     month, day, year,
  748.     hours, minutes, seconds : INTEGER;
  749.   BEGIN
  750.     GetFileDateAndTime(OldHandle, month, day, year,
  751.                           hours, minutes, seconds );
  752.     SetFileDateAndTime(NewHandle, month, day, year,
  753.                           hours, minutes, seconds );
  754.   END; {SetDateAndTime}
  755.  
  756.  
  757. LABEL
  758.   EndProcedure;
  759.  
  760. begin {CopyFile}
  761.   BytesToBeFreed := 0;
  762.   CopyFile := NoError;
  763.   FileSize := FileLength( OldHandle );
  764.   BytesToRead := FileSize;
  765.  
  766.   IF pos(':', NewFileName) = 2 THEN
  767.     TheDrive := NewFileName[1]
  768.   ELSE TheDrive := 'z';
  769.     {Determines the drive to which we're copying.
  770.     'z' means "default drive" to FreeDiskSpace.}
  771.   SavedMessage := FreeDiskSpace( TheDrive, FreeBytes );
  772.   if SavedMessage <> NoError then
  773.     goto EndProcedure;
  774.  
  775.   SavedMessage := OpenFile( NewHandle, NewFileName );
  776.   if SavedMessage = NoError then
  777.     begin
  778.       BytesToBeFreed := FileLength( NewHandle );
  779.       SavedMessage := CloseHandle( NewHandle );
  780.     end
  781.   else
  782.     begin
  783.       if SavedMessage <> FileNotFound then
  784.     goto EndProcedure;
  785.     end;
  786.  
  787.   IF FileSize > (FreeBytes + BytesToBeFreed) THEN
  788.     BEGIN
  789.       SavedMessage := AccessDenied;
  790.     {This will have to stand for 'Not enough room.'}
  791.       goto EndProcedure;
  792.     END;
  793.  
  794.   SavedMessage := CreateFile(NewHandle, NewFileName);
  795.   if SavedMessage <> NoError then
  796.     goto EndProcedure;
  797.  
  798.   REPEAT
  799.     ReadOldFile( MemoryPtr );
  800.     WriteNewFile( MemoryPtr );
  801.   UNTIL (BytesToRead <= 0);
  802.  
  803.   SetDateAndTime;
  804.  
  805.   SavedMessage := CloseHandle(NewHandle);
  806.   EndProcedure:
  807.   CopyFile := SavedMessage;
  808. end; {CopyFile}
  809.  
  810. 
  811.